#include "integrater.h"
#include <cmath>

namespace tls{
  integrater::~integrater(){}

  //diffuse
  diffuse::~diffuse(){
    for(int i=0;i<mats.size();++i) delete mats[i];
    for(int i=0;i<lights.size();++i) delete lights[i];
    delete samp;
    delete cam;
    delete aggr;
  }

  vec3 diffuse::L(ray& r)const{
    vec3 L(0,0,0);
    isect hit;
    if(aggr->intersect(r,hit)){
      
      for(int i=0;i<lights.size();++i){
	//shadow
	vec3 wi;
	vec3 light_L=lights[i]->L(hit,wi);
	ray shadow_ray(hit.p+eps*hit.n,wi,1);
	if(aggr->intersectP(shadow_ray)) continue;
	
	wi.normalize();
	vec3 wo=-r.d; wo.normalize();
	real ndotwi=dot(wi,hit.n);
	real ndotwo=dot(wo,hit.n);
	if(ndotwi>0 && ndotwo>0){
	  bxdf* const* fs=hit.mat->get_bxdfs(hit);
	  for(bxdf* const* j=fs;*j!=0;++j)
	    L+=(*j)->f(hit) * light_L * ndotwi;
	}
      }
    }//if aggr->intersect
    return L;
  }

  void diffuse::render(){
    for(int y=0;y<cam->f.height;++y){
      for(int x=0;x<cam->f.width;++x){
	
	vec3 p=cam->get_pixel_center(x,y);

	vec3 acc_L(0,0,0);
	for(int i=0;i<samp->n;++i){
	  vec3 k=cam->sx * 0.5 * cam->m.x().v();
	  vec3 j=cam->sy * 0.5 * cam->m.y().v();
	  vec3 s=samp->sample();
	  vec3 v=p + s.x * k + s.y * j;
	  ray r(cam->ori,v - cam->ori);
	  acc_L+=L(r);
	}
	acc_L/=samp->n;
	cam->f.f[y*cam->f.width + x]=acc_L;

      }//for x
    }//for y
  }

  ambient_occ::~ambient_occ(){
    for(int i=0;i<mats.size();++i) delete mats[i];
    delete samp_light;
    delete samp_aa;
    delete cam;
    delete aggr;
  }

  vec3 ambient_occ::L(ray& r)const{
    vec3 L(0,0,0);
    isect hit;
    if(aggr->intersect(r,hit)){
      vec3 samp=samp_light->sample();     
 
      vec3 w=hit.n,u=hit.tan,v=cross(w,u);
      real sin_phi=std::sin(samp.x),cos_phi=std::cos(samp.x);
      real sin_theta=std::sin(samp.y),cos_theta=std::cos(samp.y);
      vec3 wi=sin_theta*cos_phi * u +
	sin_theta*sin_phi * v +
	cos_theta * w;
      wi.normalize();
      
      ray shadow_ray(hit.p+hit.n*eps,wi);
      if(!aggr->intersectP(shadow_ray)){
	bxdf* const* fs=hit.mat->get_bxdfs(hit);
	for(bxdf* const* j=fs;*j!=0;++j)
	  if((*j)->type==bxdf::DIFFUSE)
	    L+=(*j)->f(hit) * vec3(1,1,1);
      }//shadow ray intersect
      return L;
    }//if aggr->intersect
    return L;
  }

  void ambient_occ::render(){
    for(int y=0;y<cam->f.height;++y){
      for(int x=0;x<cam->f.width;++x){
	
	vec3 p=cam->get_pixel_center(x,y);

	vec3 acc_L(0,0,0);
	for(int i=0;i<samp_aa->n;++i){
	  vec3 k=cam->sx * 0.5 * cam->m.x().v();
	  vec3 j=cam->sy * 0.5 * cam->m.y().v();
	  vec3 s=samp_aa->sample();
	  vec3 v=p + s.x * k + s.y * j;
	  ray r(cam->ori,v - cam->ori);
	  acc_L+=L(r);
	}
	acc_L/=samp_aa->n;
	cam->f.f[y*cam->f.width + x]=acc_L;

      }//for x
    }//for y
  }


}

